#ifndef _CCSYNCH_H_
#define _CCSYNCH_H_

#if defined(sun) || defined(_sun)
#    include <schedctl.h>
#endif

#include "../framework/cpp_framework.h"
#include "ITest.h"
#include "config.h"
#include "Synch_props.h"

using namespace CCP;

typedef struct HalfLockNode {
	struct LockNode *next;
	ArgVal arg_ret;
	int32_t pid;
	int32_t locked;
	int32_t completed;
} HalfLockNode;

typedef struct LockNode {
	struct LockNode *next;
	ArgVal arg_ret;
	int32_t pid;
	int32_t locked;
	int32_t completed;
	int32_t align[PAD_CACHE(sizeof(HalfLockNode))];
} LockNode;

typedef struct ThreadState {
	LockNode *next_node;
	int toggle;
#if defined(__sun) || defined(sun)
	schedctl_t *schedule_control;    
#endif
} ThreadState;

typedef struct LockStruct {
	volatile LockNode CACHE_ALIGN *Tail ;
} LockStruct;


LockStruct CACHE_ALIGN cc_enqueue_lock ;
LockStruct CACHE_ALIGN cc_dequeue_lock ;


void lock_init(LockStruct *l) {
    l->Tail = (LockNode*)getAlignedMemory(CACHE_LINE_SIZE, sizeof(LockNode));
    l->Tail->next = null;
    l->Tail->locked = false;
    l->Tail->completed = false;
#ifdef DEBUG
    l->rounds = l->counter =0;
#endif
    Memory::write_barrier(); 
}

inline static void threadStateInit(ThreadState *st_thread, LockStruct *l, int pid) {
    st_thread->next_node = (LockNode*)getAlignedMemory(CACHE_LINE_SIZE, sizeof(LockNode));
#ifdef sun
    st_thread->schedule_control = schedctl_init();
#endif
}


class CCSynchQueue : public ITest {
private:

	//helper function -----------------------------
	inline_ RetVal applyOp(LockStruct *l,  RetVal (*sfunc)(ArgVal, int,PoolStruct*),ThreadState *st_thread, ArgVal arg, int pid,PoolStruct *pool_node) {
		volatile LockNode *p;
		volatile LockNode *cur;
		register LockNode *next_node, *tmp_next;
		register int counter=0;

		next_node = st_thread->next_node;
		next_node->next = null;
		next_node->locked = true;
		next_node->completed = false;

#if defined(__sun) || defined(sun)
		schedctl_start(st_thread->schedule_control);
#endif
		cur = (LockNode * volatile)SWAPPO((void* volatile*)&l->Tail, next_node);
		cur->arg_ret = arg;
		cur->next = (LockNode *)next_node;
		st_thread->next_node = (LockNode *)cur;
	
#if defined(__sun) || defined(sun)
		schedctl_stop(st_thread->schedule_control);
#endif

		while (cur->locked) {                   // spinning
#if N_THREADS > USE_CPUS
			sched_yield();
#elif defined(sparc)
			Memory::read_write_barrier(); 
			Memory::read_write_barrier(); 
			Memory::read_write_barrier(); 
			Memory::read_write_barrier(); 
			Memory::read_write_barrier(); 
			Memory::read_write_barrier(); 
#else
			;
#endif
		}
		p = cur;                                // I am not been helped
		if (cur->completed)                     // I have been helped
			return cur->arg_ret;
#if defined(__sun) || defined(sun)
		schedctl_start(st_thread->schedule_control);
#endif
#ifdef DEBUG
		l->rounds++;
#endif
		while (p->next != null && counter < CCSIM_HELP_BOUND) {
			ReadPrefetch(p->next);
			counter++;
#ifdef DEBUG
			l->counter++;
#endif
			tmp_next = p->next;
			p->arg_ret = sfunc(p->arg_ret, p->pid,pool_node);
			p->completed = true;
			p->locked = false;
			p = tmp_next;
		}
		p->locked = false;                      // Unlock the next one
		Memory::write_barrier();
#if defined(__sun) || defined(sun)
		schedctl_stop(st_thread->schedule_control);
#endif

		return cur->arg_ret;
	}

	struct CCSynchSlotInfo {
		ThreadState  ldequeue_lock;
		ThreadState  lenqueue_lock;
		PoolStruct pool_node;

		CCSynchSlotInfo* volatile _next;	

		CCSynchSlotInfo(int id=-1) {
			init_pool(&pool_node, sizeof(ListNode));
			threadStateInit(&lenqueue_lock, &cc_enqueue_lock, (int)id);
			threadStateInit(&ldequeue_lock, &cc_dequeue_lock, (int)id);
		}
	};

	CCP::ThreadLocal<CCSynchSlotInfo*>	_tls_slot_ccsynch_info;
	CCP::AtomicReference<CCSynchSlotInfo>	_tail_ccsynch_slot;

	//list helper function --------------------------
	CCSynchSlotInfo* get_new_ccsynch_slot(int id) {
		CCSynchSlotInfo* my_slot= new CCSynchSlotInfo(id);
		_tls_slot_ccsynch_info.set(my_slot);

		CCSynchSlotInfo* curr_tail;
		do {
			curr_tail = _tail_ccsynch_slot.get();
			my_slot->_next = curr_tail;
		} while(false == _tail_ccsynch_slot.compareAndSet(curr_tail, my_slot));

		return my_slot;
	}

	inline_ static RetVal enqueue(ArgVal arg, int pid,PoolStruct* pool_node) {
		ListNode *node = (ListNode*)alloc_obj(pool_node);

		node->next = null;
		node->val = arg;
		last->next = node;
		last = node;
		return -1;
	}

	inline_ static RetVal dequeue(ArgVal arg, int pid,PoolStruct* pool_node) {
		ListNode *node = (ListNode *)first;
		if (first->next != null){
			first = first->next;
			return node->val;
		} else {
			return 0;
		}
	}

public:
	const int CCSIM_HELP_BOUND; 
	//public operations ---------------------------
	CCSynchQueue():CCSIM_HELP_BOUND(3* _NUM_THREADS) 
	{
		_tail_ccsynch_slot.set(new CCSynchSlotInfo());
		lock_init(&cc_enqueue_lock);   
		lock_init(&cc_dequeue_lock); 	
	}

	virtual ~CCSynchQueue() { }

	//enq ......................................................
	boolean add(final int iThread, final int inValue) {

		CCSynchSlotInfo* my_slot = _tls_slot_ccsynch_info.get();
		if(null == my_slot)
			my_slot = get_new_ccsynch_slot(iThread);

		applyOp(&cc_enqueue_lock, enqueue, &my_slot->lenqueue_lock, (ArgVal) inValue, iThread,&my_slot->pool_node);
		return true;	
	}

	//deq ......................................................
	int remove(final int iThread, final int inValue) {
		CCSynchSlotInfo* my_slot = _tls_slot_ccsynch_info.get();
		if(null == my_slot)
			my_slot = get_new_ccsynch_slot(iThread);

		return applyOp(&cc_dequeue_lock, dequeue, &my_slot->ldequeue_lock, (ArgVal) 1, iThread,&my_slot->pool_node);
	}

	//peek .....................................................
	int contain(final int iThread, final int inValue) {
		return _NULL_VALUE;
	}

	//general .....................................................
	int size() {
		return 0;
	}

	void entry(final int iThread,bool flag = false) {
		CCSynchSlotInfo* my_slot = _tls_slot_ccsynch_info.get();
		if(null == my_slot)
			my_slot = get_new_ccsynch_slot(iThread);
	}
	void exit(final int iThread,bool flag = false) {
	}
	final char* name() {
		return "CCSynchQueue";
	}

};

#endif

